#pragma once

#include "kdealloc.h"
#include "kstlbool.h"

#include <stdlib.h>

#ifdef __cplusplus
extern "C" {
#endif

typedef struct KstlList     KstlList;
typedef struct KstlListNode KstlListNode;

typedef struct KstlListIterator {
    KstlList*     handle;
    KstlListNode* current;
    KstlListNode* next;
} KstlListIterator;

typedef struct KstlListSettings {
    size_t          itemSize;
    KstlDeallocator dealloc;
} KstlListSettings;

typedef enum INIT_TYPE { LOCAL_INIT, EXTERNAL_INIT } INIT_TYPE;

typedef struct KstlList {
    INIT_TYPE        scope;
    KstlListSettings settings;
    size_t           size;
    KstlListNode*    head;
    KstlListNode*    tail;
} KstlList;

#define KL_FOR_EACH(handle, it, end)                                                                                   \
    for (KstlListIterator(it) = KstlListIteratorBegin((handle)), (end) = KstlListIteratorEnd((handle));                \
         !KstlListIteratorEqual((it), (end));                                                                          \
         KstlListIteratorNext(&(it)))

// Construction and destruction
KstlList* KstlListInit(KstlListSettings settings);
int       KstlListInitStruct(KstlList* out, KstlListSettings settings);
void      KstlListRelease(KstlList* handle);

// state getters
size_t KstlListSize(KstlList* handle);
bool   KstlListEmpty(KstlList* handle);

// data getters
void* KstlListFront(KstlList* handle);
void* KstlListBack(KstlList* handle);
void* KstlListPayload(KstlListNode* node);

// data setters
void* KstlListPushRawBack(KstlList* handle);
void* KstlListPushRawFront(KstlList* handle);

int KstlListPushBack(KstlList* handle, void* item);
int KstlListPopBack(KstlList* handle);

int KstlListPushFront(KstlList* handle, void* item);
int KstlListPopFront(KstlList* handle);

int KstlListErase(KstlList* handle, KstlListIterator it);

// search
KstlListIterator KstlListFind(KstlList* handle, void* item);

// Node handlers
int  KstlListExtractNode(KstlList* handle, KstlListNode* node);
void KstlListClearNode(KstlList* handle, KstlListNode* node);

int KstlListPushNodeBack(KstlList* handle, KstlListNode* node);
int KstlListPushNodeFront(KstlList* handle, KstlListNode* node);

KstlListNode* KstlListPopNodeFront(KstlList* handle);
KstlListNode* KstlListPopNodeBack(KstlList* handle);

int KstlListConcatinate(KstlList* left, KstlList* right);


// iterators
KstlListIterator KstlListIteratorBegin(KstlList* handle);
KstlListIterator KstlListIteratorEnd(KstlList* handle);

void KstlListIteratorNext(KstlListIterator* it);

KstlListIterator KstlListIteratorReverseBegin(KstlList* handle);
KstlListIterator KstlListIteratorReverseEnd(KstlList* handle);

void KstlListIteratorReverseNext(KstlListIterator* it);

bool  KstlListIteratorEqual(KstlListIterator current, KstlListIterator end);
void* KstlListIteratorValue(KstlListIterator current);

#ifdef __cplusplus
}  // extern "C"
#endif